#include <iostream>

#define GLFW_INCLUDE_NONE
#include <GLFW/glfw3.h>
#include "gl_tools.h"
#include "glm/ext/matrix_transform.hpp"
#include "glm/glm.hpp"
#include "glm/glm.hpp"
#include "glm/gtc/type_ptr.hpp"

#include <cmath>

// 全局数据
float xScale = 1.f, yScale = 1.f;
float yOffset = 0.f, xOffset = 0.f;
bool  g_inDrag = false;
int   g_win_width = 640, g_win_height = 480;
double g_clk_pos_x, g_clk_pos_y;

static void _12510_scale(float &v, bool inc) {
    static float s_inc_table[11] = {
            2,2,                // 0,1 => 2
            5,5,5,              // 2,3,4 => 5
            10,10,10,10,10,     // 5,6,7,8,9 => 10
            0                   // 10 ???
    };
    static float s_dec_table[11] = {
            0.5,0.5,            // 0,1 => 1/10
            1,                  // 2 => 1
            2,2,2,              // 3,4,5 => 2
            5,5,5,5,            // 6,7,8,9 => 5
            0,                  // 10 ???
    };

    assert (fabsf(0.f-v) > 1.e-5f);

    const int exp = (int)floorf(log10f(v));                      //  指数部分
    const int num = (int)(v / powf(10, (float)exp)); // [1-9]

    if (inc && exp < 5) {
        v = s_inc_table[num] * powf(10, (float)exp);
    } else if (!inc && exp > -5) {
        v = s_dec_table[num] * powf(10, (float)exp);
    }
    fprintf(stdout, "1,2,5,10 Scale: v = %.2f\n", v);
}

static void glfwErrorCallback(int code, const char *desc) {
    fprintf(stderr, "GLFW error(%d): %s\n", code, desc);
}

static void glfwSizeCallback(GLFWwindow*, int w, int h) {
    g_win_width = w;
    g_win_height = h;
    glViewport(0,0,(int)w,(int)h);
}

static void glfwScrollCallback(GLFWwindow* win, double x, double y) {
//    xOffset += (float)x;
//    yOffset += (float)y;
    int state = glfwGetKey(win, GLFW_KEY_LEFT_CONTROL);
    if (y > 0) {
        _12510_scale(state == GLFW_PRESS ? xScale : yScale , true);
    } else {
        _12510_scale(state == GLFW_PRESS ? xScale : yScale , false);
    }
}

static void glfwCursorPosCallback(GLFWwindow *win, double x, double y) {
//    fprintf(stdout, "Cursor: %f,%f\n", x, y);
    if (g_inDrag) {
        double diff_x = x - g_clk_pos_x;
        double diff_y = y - g_clk_pos_y;

        xOffset += (float)diff_x;
        yOffset += (float)diff_y;

        g_clk_pos_x = x;
        g_clk_pos_y = y;
    }
}

static void glfwMouseButtonCallback(GLFWwindow *win, int button, int action, int mods) {
    if (action == GLFW_RELEASE && button == GLFW_MOUSE_BUTTON_LEFT) {
        g_inDrag = false;
    } else if (action == GLFW_PRESS && button == GLFW_MOUSE_BUTTON_LEFT) {
        g_inDrag = true;
        glfwGetWindowSize(win, &g_win_width, &g_win_height);
        glfwGetCursorPos(win, &g_clk_pos_x, &g_clk_pos_y);
    } else if (action == GLFW_PRESS && button == GLFW_MOUSE_BUTTON_RIGHT) {
        xOffset = yOffset = 0.f;
    }
}

int main() {
    glfwSetErrorCallback(&glfwErrorCallback);
    if (GLFW_TRUE != glfwInit())
    {
        fprintf(stderr, "Failed to initialize GLFW!!\n");
        abort();
    }
#if (WIN32)
    glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
#else
    glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
#endif
//    glfwWindowHint(GLFW_MAXIMIZED, GLFW_TRUE);
    glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_TRUE);

    GLFWwindow *win = glfwCreateWindow(g_win_width, g_win_height, "Wave", nullptr, nullptr);
    glfwMakeContextCurrent(win);

    // setup window callbacks
    glfwSetWindowSizeCallback(win, glfwSizeCallback);
    glfwSetScrollCallback(win, glfwScrollCallback);
    glfwSetCursorPosCallback(win, glfwCursorPosCallback);
    glfwSetMouseButtonCallback(win, glfwMouseButtonCallback);

    glfwSwapInterval(1);

    gladLoadGLLoader((GLADloadproc) glfwGetProcAddress);
    {
        Program prog("../triangle.gl");

        float vertex[3 * 3] = {
             0.0f,  0.0f, 0.0f,
             100.f,  100.f, 0.0f,
             0.0f,  100.f, 0.0f,
        };

        float color[3 * 3] = {
            1.0f, 0.0f, 0.0f, // red
            0.0f, 1.0f, 0.0f, // green
            0.0f, 0.0f, 1.0f, // blue
        };

        // 提交数据到缓冲区对象
        VBO combind(GL_ARRAY_BUFFER, GL_STATIC_DRAW);
        combind.Bind();
        combind.Allocate(sizeof(float)*18);
        combind.Update(0, &vertex[0], sizeof(vertex));
        combind.Update(sizeof(vertex), &color[0], sizeof(color));

        // 获取属性位置
        GLint loc_pos, loc_col, loc_mvp;
        loc_pos = prog.AttribLocation("iPos");
        loc_col = prog.AttribLocation("iColor");
        loc_mvp = prog.UniformLocation("mvp");

        while (!glfwWindowShouldClose(win)) {
            // 清除屏幕
            GL_CHECK(glClearColor(0.0,0.0,0.0,1.0));
            GL_CHECK(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT));
            GL_CHECK(glEnable(GL_BLEND));
            GL_CHECK(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));

            // 累积变换
            glm::mat4 ortho = glm::ortho(0.f, (float)g_win_width, (float)g_win_height, 0.f, -1.f, 1.f);
            glm::mat4 trans = glm::translate(glm::mat4(1.f), glm::vec3(xOffset, yOffset, 0.f));
            glm::mat4 scale = glm::scale(glm::mat4(1.f), glm::vec3(xScale, yScale, 1.0));
//            glm::mat4 mvp = glm::rotate(
//                    glm::mat4(1.0f),
//                    glm::radians((float)glfwGetTime() * 200),
//                    glm::vec3(0.f,0.f,1.f)
//                    );
            glm::mat4 mvp = ortho * scale * trans;
            prog.Use();
            glUniformMatrix4fv(loc_mvp, 1, GL_FALSE, glm::value_ptr(mvp));
            combind.Bind();

            // 配置属性
            GL_CHECK(glEnableVertexAttribArray(loc_pos));
            GL_CHECK(glEnableVertexAttribArray(loc_col));
            GL_CHECK(glVertexAttribPointer(loc_pos, 3, GL_FLOAT, GL_FALSE, 0, (const void*)0));
            GL_CHECK(glVertexAttribPointer(loc_col, 3, GL_FLOAT, GL_FALSE, 0, (const void*)sizeof(vertex)));

            // 绘制
            GL_CHECK(glDrawArrays(GL_TRIANGLES, 0, 3));

            GL_CHECK(glDisableVertexAttribArray(loc_pos));
            GL_CHECK(glDisableVertexAttribArray(loc_col));

            // 交换缓冲区
            glfwSwapBuffers(win);
            glfwPollEvents();
        }
    }
    glfwDestroyWindow(win);
    glfwTerminate();

    return 0;
}
